package com.rtsapp.server.simulator;

import com.rtsapp.server.common.ByteBuffer;
import com.rtsapp.server.network.protocol.command.ICommand;
import com.rtsapp.server.network.protocol.crypto.client.ClientRSAHandshakeKey;
import com.rtsapp.server.simulator.socket.SimulatorNettySocket;
import com.rtsapp.server.utils.StringUtils;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public abstract class AbstractGameClient implements Runnable, IDataReceive, CommandFormater.InputFilter {

    protected final Scanner input = new Scanner(System.in);
    protected final Map<Integer, String> commandsMap = new HashMap<>();
    protected final BlockingQueue<ICommand> receiveCommandQueue = new ArrayBlockingQueue<ICommand>(1024);
    private final String projectCommandPackage;
    private final CommandFormater commandFormator;

    private final ClientRSAHandshakeKey handshakeKey;

    protected ITcpSocket socket;
    private String ip;
    private String port;


    public AbstractGameClient(String projectCommandPackage, ClientRSAHandshakeKey handshakeKey ) {
        this.projectCommandPackage = projectCommandPackage;
        this.commandFormator = new CommandFormater(projectCommandPackage, this );
        this.handshakeKey = handshakeKey;
    }

    public AbstractGameClient(String projectCommandPackage){
        this( projectCommandPackage, null );
    }

    public void setIp( String ip, int port ){
        this.ip = ip;
        this.port = String.valueOf( port );
    }



    public abstract void processRecievedCommand(ICommand command);

    public abstract void processSendCommand(ByteBuffer buffer, ICommand command);

    public void start() throws Exception {

        System.out.println("\nLoading...");
        try {
            Class<?> commands = Class.forName(projectCommandPackage + ".Commands");
            for (Field field : commands.getFields()) {
                if (field.getName().startsWith("MODULE_")) {
                    continue;
                }
                commandsMap.put(Integer.parseInt(field.get(null).toString()), field.getName());
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }


        int inputChar;
        if( ip == null ) {
            while (true) {
                System.out.print("\nTarget host: ");
                ip = input.nextLine();
                if (StringUtils.isEmpty(ip)) {
                    while (true) {
                        System.out.print("\nThe default is the host you want to use '127.0.0.1' [y/n]?");
                        inputChar = System.in.read();
                        if (inputChar == 'y' || inputChar == 'Y') {
                            ip = "127.0.0.1";
                            break;
                        } else if (inputChar == 'n' || inputChar == 'N') {
                            input.nextLine();
                            break;
                        } else {
                            continue;
                        }
                    }
                    if (inputChar == 'y' || inputChar == 'Y') {
                        input.nextLine();
                        break;
                    }
                } else {
                    break;
                }
            }
        }

        if( port == null ) {
            while (true) {
                System.out.print("\nTarget port: ");
                port = input.nextLine();
                if (StringUtils.isEmpty(port)) {
                    while (true) {
                        System.out.print("\nThe default is the port you want to use '20002' [y/n]?");
                        inputChar = System.in.read();
                        if (inputChar == 'y' || inputChar == 'Y') {
                            port = "20002";
                            break;
                        } else if (inputChar == 'n' || inputChar == 'N') {
                            input.nextLine();
                            break;
                        } else {
                            continue;
                        }
                    }
                    if (inputChar == 'y' || inputChar == 'Y') {
                        input.nextLine();
                        break;
                    }
                } else {
                    break;
                }
            }
        }

        System.out.println("\nTrying to connect...");
        // 连接服务器
        socket = new SimulatorNettySocket(ip, Integer.parseInt(port), handshakeKey);
        socket.setReceive( this );
        socket.connect();

        // 启动解析线程
        Thread resolveThread = new Thread(this, "ResolveThread" );
        resolveThread.start();

        while (true) {

            System.out.println();

            ICommand command = commandFormator.getCommandFromCmd(input);

            if (command != null) {
                sendData(command);
            }

        }
    }

    @Override
    public void run() {

        while (true) {

            // 打印
            ICommand command = null;
            try {
                command = receiveCommandQueue.take();
                String commandName = commandsMap.get(command.getCommandId());

                List<Boolean> isLast = new ArrayList<>();
                isLast.add(true);
                commandFormator.writeCommandToCmd(System.out, command, commandName,  isLast);


                processRecievedCommand(command);


                System.out.println();
                System.out.println();

            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }


    @Override
    public void receiveData(byte[] bytes) {

        // 从bytes中尝试夺取命令,并加入commandList
        try {
            ByteBuffer buffer = new ByteBuffer(bytes);
            int commandId = buffer.getInt(0);
            ICommand command = (ICommand) Class.forName(String.format(this.projectCommandPackage + ".%sResponse", commandsMap.get(commandId))).newInstance();
            command.readFromBuffer(buffer);
            receiveCommandQueue.put(command);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void sendData(ICommand command) {
        ByteBuffer buffer = new ByteBuffer();
        // 写入sessionid
        buffer.writeInt(0);

        processSendCommand(buffer, command);

        command.writeToBuffer(buffer);

        buffer.setInt(0, buffer.readableBytes() - 4); // 长度

        // 写入命令
        socket.send(buffer.array(), buffer.readableBytes());
    }


}
